package com.nervenets.shopping.controller.order;

import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.nervenets.general.config.ApplicationProperties;
import com.nervenets.general.entity.ResponseResult;
import com.nervenets.general.exception.LogicException;
import com.nervenets.general.jwt.aspect.JwtRole;
import com.nervenets.general.jwt.aspect.JwtSecurity;
import com.nervenets.general.jwt.util.JwtUtils;
import com.nervenets.general.redis.RedisSyncLocker;
import com.nervenets.general.utils.JodaUtils;
import com.nervenets.general.utils.SpringContextHolder;
import com.nervenets.general.web.BaseEntityController;
import com.nervenets.general.web.params.IdParams;
import com.nervenets.general.web.params.IdsParams;
import com.nervenets.shopping.controller.order.params.GroupBuyOrderUserExpressParams;
import com.nervenets.shopping.controller.order.params.GroupBuyOrderUserFormParams;
import com.nervenets.shopping.controller.order.params.GroupBuyOrderUserPagingParams;
import com.nervenets.shopping.enumeration.OrderStatus;
import com.nervenets.shopping.enumeration.PayType;
import com.nervenets.shopping.event.DataOfUserEvent;
import com.nervenets.shopping.event.GroupBuyOrderSuccessEvent;
import com.nervenets.shopping.hibernate.domain.order.GroupBuyOrderUser;
import com.nervenets.shopping.hibernate.domain.user.UserWxId;
import com.nervenets.shopping.service.GroupBuyOrderService;
import com.nervenets.shopping.service.GroupBuyOrderUserService;
import com.nervenets.shopping.service.ProductService;
import com.nervenets.shopping.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.List;

@Slf4j
@Api(value = "拼团订单管理接口v1", tags = "拼团订单管理相关接口")
@RestController
@AllArgsConstructor
@RequestMapping("/api/groupbuy/order/user")
@JwtRole(group = "orders", groupName = "订单管理", function = "user", functionName = "拼团订单管理", queue = 203)
public class GroupBuyOrderUserController extends BaseEntityController<GroupBuyOrderUser, GroupBuyOrderUserService, GroupBuyOrderUserFormParams, GroupBuyOrderUserPagingParams, GroupBuyOrderUser, GroupBuyOrderUser> {
    private final WxPayService wxPayService;
    private final UserService userService;
    private final ApplicationProperties applicationProperties;
    private final GroupBuyOrderService groupBuyOrderService;
    private final RedisSyncLocker<Void> redisSyncLocker;
    private final RedisSyncLocker<ResponseEntity<ResponseResult<GroupBuyOrderUser>>> groupBuyPaySyncLocker;
    private final ProductService productService;

    @Override
    public ResponseEntity<ResponseResult<GroupBuyOrderUser>> edit(@Valid @RequestBody GroupBuyOrderUserFormParams params, HttpServletRequest request, HttpServletResponse response) {
        return super.edit(params, request, response);
    }

    @Override
    public ResponseEntity<ResponseResult<Void>> del(IdsParams params, HttpServletRequest request, HttpServletResponse response) {
        throw new LogicException("拼团订单不支持删除");
    }

    @ApiOperation(value = "查询拼团成功的通知")
    @PostMapping("/grouped/notify")
    public ResponseEntity<ResponseResult<List<GroupBuyOrderUser>>> groupedNotify() {
        return successMessage(service.findAll((predicates, root, query, builder) -> {
            predicates.add(builder.equal(root.get("createBy"), JwtUtils.getUser().getId()));
            predicates.add(builder.greaterThan(root.get("groupedTime"), 0));
            predicates.add(builder.isFalse(root.get("notify")));
        }));
    }

    @ApiOperation(value = "确认通知")
    @PostMapping("/notify/confirm")
    public ResponseEntity<ResponseResult<GroupBuyOrderUser>> notifyConfirm(@Valid @RequestBody IdParams params) {
        GroupBuyOrderUser orderUser = service.findOne(params.getTargetId(), "订单不存在");
        orderUser.setNotify(true);
        return successMessage(service.save(orderUser));
    }

    @ApiOperation(value = "发货")
    @PostMapping("/express")
    @JwtSecurity(permission = "express", permissionName = "发货")
    public ResponseEntity<ResponseResult<GroupBuyOrderUser>> enable(@Valid @RequestBody GroupBuyOrderUserExpressParams params) {
        GroupBuyOrderUser orderUser = service.findOne(params.getTargetId(), "订单不存在");
        orderUser.setExpressTime(JodaUtils.getTimestamp());
        orderUser.setExpressName(params.getExpressName());
        orderUser.setExpressNumber(params.getExpressNumber());
        orderUser.setStatus(OrderStatus.shipped);
        return successMessage(service.save(orderUser));
    }

    @ApiOperation(value = "确认收货")
    @PostMapping("/finish")
    public ResponseEntity<ResponseResult<GroupBuyOrderUser>> finish(@Valid @RequestBody IdParams params) {
        GroupBuyOrderUser orderUser = service.findOne(params.getTargetId(), "订单不存在");
        orderUser.setStatus(OrderStatus.finish);
        return successMessage(service.save(orderUser));
    }

    @ApiOperation(value = "支付")
    @PostMapping("/pay")
    public ResponseEntity<ResponseResult<GroupBuyOrderUser>> enable(@Valid @RequestBody IdParams params, HttpServletRequest request) throws Exception {
        return groupBuyPaySyncLocker.idLock(params.getTargetId(), () -> {
            GroupBuyOrderUser groupBuyOrderUser = service.findOne(params.getTargetId(), "您要支付的订单不存在");
            final String notifyUrl = String.format("http://%s/api/groupbuy/order/user/wx/callback", applicationProperties.getBaseDomain());
            UserWxId userWxId = userService.findUserWxId(userService.findOne(groupBuyOrderUser.getCreateBy()), wxPayService.getConfig().getAppId());
            try {
                groupBuyOrderUser.setThirdPayInfo(service.wxJsapiPay(
                        groupBuyOrderUser.getProductName(),
                        groupBuyOrderUser.getTradeNo(),
                        groupBuyOrderUser.getProduct().getPrice(),
                        userWxId.getOpenId(),
                        notifyUrl, request));
            } catch (WxPayException e) {
                return errorMessage(e.getErrCodeDes());
            }
            return successMessage(groupBuyOrderUser);
        });
    }

    @ApiOperation(value = "假装支付成功")
    @PostMapping("/mockPay")
    @JwtSecurity(required = false)
    public ResponseEntity<ResponseResult<GroupBuyOrderUser>> mockPay(@Valid @RequestBody IdParams params) throws Exception {
        GroupBuyOrderUser order = service.findOne(params.getTargetId(), "订单不存在");
        if (OrderStatus.init.equals(order.getStatus()) || OrderStatus.cancel.equals(order.getStatus())) {
            order.setPayTime(JodaUtils.getTimestamp());
            order.setStatus(OrderStatus.paid);
            order.setPayType(PayType.wxpay_online);
            service.save(order);
            boolean grouped = groupBuyOrderService.join(order);
            SpringContextHolder.publishEvent(new DataOfUserEvent(DataOfUserEvent.Type.pay, order.getId(), order.getCreateBy()));
            if (grouped) {
                SpringContextHolder.publishEvent(new GroupBuyOrderSuccessEvent(order.getId()));
            }
        }
        return successMessage(order);
    }

    @ApiOperation(value = "补团")
    @PostMapping("/reJoin")
    @JwtSecurity(required = false)
    public ResponseEntity<ResponseResult<Boolean>> reJoin(@Valid @RequestBody IdParams params) {
        GroupBuyOrderUser order = service.findOne(params.getTargetId(), "订单不存在");
        boolean grouped = groupBuyOrderService.join(order);
        if (grouped) {
            SpringContextHolder.publishEvent(new GroupBuyOrderSuccessEvent(order.getId()));
        }
        return successMessage(grouped);
    }

    @ApiOperation("微信订单回调")
    @PostMapping("/wx/callback")
    @JwtSecurity(required = false)
    public String orderWxCallback(final HttpServletRequest request, HttpServletResponse response) {
        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(xmlResult);
            log.info("微信回调结果: {}", result);
            // 结果正确
            String orderId = result.getOutTradeNo();
            String tradeNo = result.getTransactionId();
            final Integer totalFee = result.getTotalFee();
            final String openid = result.getOpenid();
            if ("SUCCESS".equals(result.getReturnCode()) && "SUCCESS".equals(result.getResultCode())) {
                long id = Long.parseLong(orderId.substring(GroupBuyOrderUser.TRADE_NO_PREFIX.length() + 5));
                //自己处理订单的业务逻辑，需要判断订单是否已经支付过，否则可能会重复调用
                redisSyncLocker.idLock(id, () -> {
                    service.findById(id).ifPresent(order -> {
                        if (OrderStatus.init.equals(order.getStatus()) || OrderStatus.cancel.equals(order.getStatus())) {
                            order.setPayTime(JodaUtils.getTimestamp());
                            order.setOutTradeNo(tradeNo);
                            order.setStatus(OrderStatus.paid);
                            order.setPayType(PayType.wxpay_online);
                            service.save(order);
                            boolean grouped = groupBuyOrderService.join(order);
                            SpringContextHolder.publishEvent(new DataOfUserEvent(DataOfUserEvent.Type.pay, order.getId(), order.getCreateBy()));
                            productService.updateSaleCount(order.getProductId(), service.count((predicates, root, query, builder) -> {
                                predicates.add(builder.equal(root.get("product").get("id"), order.getProductId()));
                                predicates.add(builder.greaterThanOrEqualTo(root.get("status"), OrderStatus.paid));
                            }));
                            userService.updateGroupCountAndTime(order.getCreateBy(), service.count((predicates, root, query, builder) -> {
                                predicates.add(builder.equal(root.get("createBy"), order.getCreateBy()));
                                predicates.add(builder.greaterThanOrEqualTo(root.get("status"), OrderStatus.paid));
                            }), JodaUtils.getTimestamp());
                            if (grouped) {
                                SpringContextHolder.publishEvent(new GroupBuyOrderSuccessEvent(order.getId()));
                            }
                        }
                    });
                    return null;
                });
            }
            return WxPayNotifyResponse.success("处理成功!");
        } catch (Exception e) {
            log.error("微信回调结果异常,异常原因....", e);
            return WxPayNotifyResponse.fail(e.getMessage());
        }
    }
}
